import de.looksgood.ani.*;
import de.looksgood.ani.easing.*;

static int chances[][] = {
   /*0*/ {0,   0,   1,   0,   0,   1,   1,   0,   0,   0},
   /*1*/ {0,   0,   1,   0,   0,   1,   0,   1,   1,   0},
   /*2*/ {1,   1,   0,   0,   0,   0,   0,   0,   0,   1},
   /*3*/ {0,   0,   0,   0,   1,   0,   1,   0,   1,   0},
   /*4*/ {0,   0,   0,   1,   0,   0,   0,   1,   0,   0},
   /*5*/ {1,   1,   0,   0,   0,   0,   0,   0,   1,   1},
   /*6*/ {1,   0,   0,   1,   0,   0,   0,   1,   0,   0},
   /*7*/ {0,   1,   0,   0,   1,   0,   1,   0,   0,   0},
   /*8*/ {0,   1,   0,   1,   0,   1,   0,   0,   0,   0},
   /*9*/ {0,   0,   1,   0,   0,   1,   0,   0,   0,   0}
};

int numOfNodes = 10;
Circle[] circles;
float centerX, centerY;
float radiuss[] = {
    0, 0, 80, 160, 240, 320, 400, 480, 560
};
float[]x;
float[]y;
float radius = 40;
int hie[];
int queue[];
boolean visited[];
int sameLevel[];
int level[][];
void bfs(int snode)
{
    queue = new int[numOfNodes * 2];
    visited = new boolean[numOfNodes];
    hie = new int[numOfNodes];
    sameLevel = new int[numOfNodes];
    int head = 0;
    int tail = 0;
    queue[head] = snode;
    ++head;
    hie[snode] = 1;
    ++sameLevel[hie[snode]];
    visited[snode] = true;
    while (head != tail) {
        int curnode = queue[tail];
        ++tail;
        int curhie = hie[curnode];
        //println("curnode " + curnode + " curhie:" + curhie);
        for (int i=0; i<numOfNodes; ++i) if (i != curnode) {
            if (chances[curnode][i] == 1 && !visited[i]) {
                int newhie = curhie + 1;
                ++sameLevel[newhie];
                hie[i] = newhie;
                queue[head] = i;
                ++head;
                visited[i] = true;
            }
        }        
    }
}

float pos[][];

void prepareGraph()
{
    pos = new float[numOfNodes][2];
    level = new int[numOfNodes][numOfNodes];
    for (int i=0; i<numOfNodes; ++i) {
        level[hie[i]][++level[hie[i]][0]] = i;
    }
    for (int i=0; i<numOfNodes; ++i) if (level[i][0] > 0) {
        for (int j=1; j<=level[i][0]; ++j) {
            float theta = map(j, 0, level[i][0], 0, 2 * PI);
            PVector pv = new PVector(centerX + radiuss[i] * cos(theta),
                                     centerY + radiuss[i] * sin(theta));
            pos[level[i][j]][0] = pv.x;
            pos[level[i][j]][1] = pv.y;
        }
    }
}

void setup()
{
    size(800, 800);
    centerX = width/2;
    centerY = height/2;
    x = new float[numOfNodes];
    y = new float[numOfNodes];
    textAlign(CENTER);
    bfs(0);
    prepareGraph();
    //Motion.setup(this);
    Ani.init(this);
    circles = new Circle[numOfNodes];
    for (int i=0; i<numOfNodes; ++i) {
        circles[i] = new Circle(pos[i][0], pos[i][1], i);
    }
}

float distance(float x1, float y1, float x2, float y2)
{
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}


boolean isInside(float x, float y, int id)
{
    float cx = pos[id][0];
    float cy = pos[id][1];
    float dis = distance(x, y, cx, cy);
    return (dis < radius);
}

int detectSelection()
{
    for (int i=0; i<numOfNodes; ++i) {
        if (isInside(mouseX, mouseY, i)) 
            return i;
    }
    return -1;
}

void drawGraph()
{
    fill(0);
    for (int i=0; i<numOfNodes; ++i) {
        for (int j=0; j<numOfNodes; ++j) if (chances[i][j] == 1) {
            line(circles[i].x, circles[i].y, circles[j].x, circles[j].y);
        }
    }
    for (int i=0; i<numOfNodes; ++i) {
        circles[i].draw();
    }
}

void draw()
{
    background(255);
    stroke(120, 20, 200, 90);
    noFill();
    for (int i=2; i<6; ++i) {
        ellipse(centerX, centerY, radiuss[i]*2, radiuss[i]*2);
    }
    drawGraph();
}

void mouseReleased() {
    int id = detectSelection();
    if (id >= 0) {
        println("node selected: " + id);
        bfs(id);
        prepareGraph();
        for (int i=0; i<numOfNodes; ++i) {
            circles[i].setAni(pos[i][0], pos[i][1]);
        }
    }
}

class Circle {
    public float x;
    public float y;
    public float diameter;
    public int id;
    
    public Circle(float _x, float _y, int _id) {
        diameter = 20;
        id = _id;
        x = _x;
        y = _y;
    }
    
    public void setAni(float _x, float _y) {
        Ani.to(this, 1.5, "x", _x, Ani.EXPO_IN_OUT);
        Ani.to(this, 1.5, "y", _y, Ani.EXPO_IN_OUT);
    }
       
    void draw() {
        fill(255);
        ellipse(x, y, radius, radius);
        fill(0);
        text(""+id, x, y+5);
    }
}


